#include "TrafficControl.h"

#include <QEvent>
#include "NetworkReplyAdaptor.h"


#include <QTcpSocket>

QT_BEGIN_INCLUDE_NAMESPACE
#include <private/qobject_p.h>
#include <private/qauthenticator_p.h>
#include <private/qhttpnetworkconnectionchannel_p.h>
#include <private/qhttpthreaddelegate_p.h>
QT_END_INCLUDE_NAMESPACE

#include "QConnectionObjectEx.h"

#include <functional>
#include <string.h>

class SocketReadNotifyData
{
public:
	SocketReadNotifyData()
		: m_interceptor(0)
	{
	}

	SocketReadNotifyData(const std::shared_ptr<ISocketReadInterceptor>& interceptor)
		: m_interceptor(interceptor)
	{
	}

	bool isValid() const
	{
		return m_interceptor != 0;
	}

	bool eventFilter(QSocketNotifier* notifier, QEvent* event)
	{
		return event->type() == QEvent::SockAct
			   && isValid()
			   && m_interceptor->intercept(notifier, event);
	}

private:
	std::shared_ptr<ISocketReadInterceptor> const m_interceptor;
};

Q_DECLARE_METATYPE(SocketReadNotifyData);


const QNetworkRequest::Attribute kSocketReadNotifyData = (QNetworkRequest::Attribute) (QNetworkRequest::User + 1);

template <class T> struct Caster : public std::unary_function<QObject*, T*>
{
	T* operator()(QObject* item)const { return (item != 0) ? dynamic_cast<T*>(item) : 0; }
};

template <class T> struct ParentCaster : public std::unary_function<QObject*, T*>
{
	T* operator()(QObject* item)const { return (item != 0) ? dynamic_cast<T*>(item->parent()) : 0; }
};

struct IsHttpNetworkConnectionChannel : public std::unary_function<QObject*, QHttpNetworkConnectionChannel*>
{
	QHttpNetworkConnectionChannel* operator()(QObject* item) const
	{
		if (0 == item || 0 != strcmp("QHttpNetworkConnectionChannel", item->metaObject()->className()))
		{
			return 0;
		}
		return static_cast<QHttpNetworkConnectionChannel*>(item);
	}
};

bool handleSocketReadNotify(QObject* receiver, QEvent* e)
{
	if (QEvent::SockAct == e->type())
	{
		QSocketNotifier* notifier = qobject_cast<QSocketNotifier*>(receiver);
		if (notifier != 0 && notifier->isEnabled())
		{
			QSocketNotifier::Type type = notifier->type();
			if (QSocketNotifier::Read == type && receiver->parent() != 0)
			{
				QTcpSocket* tcpSocket = qobject_cast<QTcpSocket*>(receiver->parent()->parent());
				if (tcpSocket != 0)
				{

					QHttpNetworkConnectionChannel* channel = findFirstThat(tcpSocket, "readyRead()", IsHttpNetworkConnectionChannel());

					if (0 == channel)
					{
						// presumably SSL stuff
						QTcpSocket* sslSocket = qobject_cast<QTcpSocket*>(tcpSocket->parent());
						channel = findFirstThat(sslSocket, "readyRead()", IsHttpNetworkConnectionChannel());
					}

					if (channel != 0 && channel->reply != 0)
					{
						QObject* delegate = channel->reply->parent();
#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
						QNetworkReply* reply = findFirstThat(delegate, "downloadData(QByteArray)", ParentCaster<QNetworkReply>());
#else
						QNetworkReply* reply = findFirstThat(delegate, "downloadData(QByteArray)", Caster<QNetworkReply>());
#endif
						if (reply != 0)
						{
							QVariant var(reply->request().attribute(kSocketReadNotifyData));
							if (var.canConvert<SocketReadNotifyData>())
							{
								SocketReadNotifyData& data = *(SocketReadNotifyData*) var.data();
								if (data.isValid())
								{
									QHttpNetworkReplyPrivate* replyPrivate
										= (QHttpNetworkReplyPrivate*)((QConnectionObjectEx*) channel->reply)->dFunc();

									if (!replyPrivate->downstreamLimited)
									{
										QObject::disconnect(channel->reply, SIGNAL(readyRead()), delegate, SLOT(readyReadSlot()));
										NetworkReplyAdaptor* networkReplyAdaptor = new NetworkReplyAdaptor(channel->reply);
										QObject::connect(tcpSocket, SIGNAL(readyRead()), networkReplyAdaptor, SLOT(readyReadSlot()));

										replyPrivate->downstreamLimited = true;
									}

									return data.eventFilter(notifier, e);
								}
							}
						}
					}
				}
			}
		}
	}

	return false;
}


void subscribeSocketReadInterceptor(QNetworkRequest* request, const std::shared_ptr<ISocketReadInterceptor>& interceptor)
{
	if (0 == request || 0 == interceptor || interceptor->isInterceptorSet)
	{
		return;
	}

	request->setAttribute(kSocketReadNotifyData, QVariant::fromValue<SocketReadNotifyData>(SocketReadNotifyData(interceptor)));

	interceptor->isInterceptorSet = true;
}

